<?php
// +-------------------------------------------------------------------------------
// | DingZone [ A Rapid Development Framework For Dingtalk ]
// +-------------------------------------------------------------------------------
// | 模块信息加载器 v1.0
// |
// | Copyright (c) 2014-2018 http://www.heimalanshi.com All rights reserved.
// | Author : Jie
// | Date : 2018-08-09
// +-------------------------------------------------------------------------------

namespace module;

use common\Xml;

/**
 * 模块加载器
 */
class ModuleLoader {
    private $dir;

    /**
     * 构造函数
     *
     * @param boolean $dir
     */
    public function __construct($dir = false) {
        if (!$dir) {
            $this->dir = realpath(__DIR__ . "/../../../addons");
        } else {
            $this->dir = $dir;
        }
    }

    /**
     * 扫描addons目录
     *
     * @return array 模块名称数组
     */
    public function scan() {
        $moduleNames = [];
        $files = scandir($this->dir);
        foreach ($files as $file) {
            if ($file == "." || $file == "..") {
                continue;
            }
            if (is_dir($this->dir . "/" . $file)) {
                $xmlFile = $this->dir . "/" . $file . "/module.xml";
                if (file_exists($xmlFile)) {
                    $moduleNames[] = $file;
                }
            }
        }

        return $moduleNames;
    }

    /**
     * 获取模块信息
     *
     * @param string $moduleName
     * @return array 模块信息
     */
    public function info($moduleName) {
        $moduleDir = $this->dir . "/" . $moduleName;
        if (!file_exists($moduleDir)) {
            // 目录不存在
            return [];
        }

        $xmlFile = $moduleDir . "/module.xml";

        if (!file_exists($xmlFile)) {
            //说明文件不存在
            return [
                'name' => $moduleName
            ];
        }

        libxml_disable_entity_loader(false);
        $doc = new \DOMDocument();
        $doc->load($xmlFile);
        $moduleNode = $doc->getElementsByTagName("module")->item(0);
        $title = Xml::getXmlNodeValue($moduleNode, "title");
        $description = Xml::getXmlNodeValue($moduleNode, "description");
        $author = Xml::getXmlNodeValue($moduleNode, "author");
        $url = Xml::getXmlNodeValue($moduleNode, "url");
        $version = Xml::getXmlNodeValue($moduleNode, "version", "1.0");
        $apis = $this->_getApisXmlNodeValueList($moduleNode);
        $menus = $this->_getMenusXmlNodeValueList($moduleNode);
        $entries = $this->getEntriesXmlNodeValueList($moduleNode);
        $authorities = $this->_getAuthoritiesXmlNodeValueList($moduleNode);
        $configs = $this->_getConfigsXmlNodeValueList($moduleNode);
        $widgets = $this->_getWidgetsXmlNodeValueList($moduleNode);
        $cronTasks = $this->_getCronTasksXmlNodeValueList($moduleNode);
        $microapp = $this->_getMicroapp($moduleNode);

        return [
            'name' => $moduleName,
            'version' => $version,
            'title' => $title,
            'description' => $description,
            'author' => $author,
            'url' => $url,
            'apis' => $apis,
            'menus' => $menus,
            'entries' => $entries,
            'authorities' => $authorities,
            'configs' => $configs,
            'widgets' => $widgets,
            'cronTasks' => $cronTasks,
            'microapp' => $microapp
        ];
    }

    /**
     * 获取api节点
     *
     * @param $moduleNode
     * @return array
     */
    private static function _getApisXmlNodeValueList(\DOMElement $moduleNode) {
        if (!$moduleNode) {
            return [];
        }
        $apisNodes = $moduleNode->getElementsByTagName("apis");
        if (empty($apisNodes) || !$apisNodes->item(0)) {
            return [];
        }

        $apis = [];
        $apiNodes = $apisNodes->item(0)->getElementsByTagName("api");
        foreach ($apiNodes as $apiNode) {
            $fields = Xml::getXmlNodeValueList($apiNode, "fields", "field");
            $apis[] = [
                'api' => Xml::getNodeAttrValue($apiNode, 'name'),
                'grant_fields' => join(',', $fields)
            ];
        }

        return $apis;
    }

    /**
     * @param $moduleNode
     * @return array
     */
    private static function _getMenusXmlNodeValueList(\DOMElement $moduleNode) {
        if (!$moduleNode) {
            return [];
        }
        $menusNodes = $moduleNode->getElementsByTagName("menus");
        if (empty($menusNodes) || !$menusNodes->item(0)) {
            return [];
        }

        $menus = [];
        $menuNodes = Xml::getChildNodesByTagName($menusNodes->item(0), "menu");
        foreach ($menuNodes as $menuNode) {
            $menus[] = self::_getMenuInfo($menuNode);
        }
        return $menus;
    }

    /**
     * @param $menuNode
     * @return array
     */
    private static function _getMenuInfo(\DOMElement $menuNode) {
        $menu = [
            'title' => Xml::getNodeAttrValue($menuNode, 'title'),
            'link' => Xml::getNodeAttrValue($menuNode, 'link'),
            'img' => Xml::getNodeAttrValue($menuNode, 'img'),
            'authority' => Xml::getNodeAttrValue($menuNode, 'authority')
        ];

        $subMenuNodes = Xml::getChildNodesByTagName($menuNode, "menu");
        if (!empty($subMenuNodes)) {
            $subMenus = [];
            foreach ($subMenuNodes as $subMenuNode) {
                $subMenus[] = self::_getMenuInfo($subMenuNode);
            }
            $menu['sub'] = $subMenus;
        }

        return $menu;
    }


    /**
     * @param $moduleNode
     * @return array
     */
    private static function getEntriesXmlNodeValueList(\DOMElement $moduleNode) {
        if (!$moduleNode) {
            return [];
        }
        $entriesNodes = $moduleNode->getElementsByTagName("entries");
        if (empty($entriesNodes) || !$entriesNodes->item(0)) {
            return [];
        }

        $entries = [];
        $entryNodes = $entriesNodes->item(0)->getElementsByTagName("entry");
        foreach ($entryNodes as $entryNode) {
            $entries[] = [
                'title' => Xml::getNodeAttrValue($entryNode, 'title'),
                'link' => Xml::getNodeAttrValue($entryNode, 'link'),
                'img' => Xml::getNodeAttrValue($entryNode, 'img'),
                'authority' => Xml::getNodeAttrValue($entryNode, 'authority')
            ];
        }

        return $entries;
    }


    /**
     * @param $moduleNode
     * @return array
     */
    private static function _getAuthoritiesXmlNodeValueList(\DOMElement $moduleNode) {
        if (!$moduleNode) {
            return [];
        }
        $authoritiesNodes = $moduleNode->getElementsByTagName("authorities");
        if (empty($authoritiesNodes) || !$authoritiesNodes->item(0)) {
            return [];
        }

        $authorities = [];
        $authorityNodes = $authoritiesNodes->item(0)->getElementsByTagName("authority");
        foreach ($authorityNodes as $authorityNode) {
            $authorities[] = [
                'title' => Xml::getNodeAttrValue($authorityNode, 'title'),
                'key' => Xml::getNodeAttrValue($authorityNode, 'key'),
                'group' => Xml::getNodeAttrValue($authorityNode, 'group', ''),
                'type' => Xml::getNodeAttrValue($authorityNode, 'type', ''),
                'defaultval' => Xml::getNodeAttrValue($authorityNode, 'defaultval', '*')
            ];
        }

        return $authorities;
    }

    /**
     * 获取配置节点
     *
     * @param $moduleNode
     * @return array
     */
    private static function _getConfigsXmlNodeValueList(\DOMElement $moduleNode) {
        if (!$moduleNode) {
            return [];
        }
        $configsNodes = $moduleNode->getElementsByTagName("configs");
        if (empty($configsNodes) || !$configsNodes->item(0)) {
            return [];
        }

        $configs = [];
        $configNodes = $configsNodes->item(0)->getElementsByTagName("config");
        foreach ($configNodes as $configNode) {
            $config = [
                'title' => Xml::getNodeAttrValue($configNode, 'title'),
                'key' => Xml::getNodeAttrValue($configNode, 'key'),
                'group' => Xml::getNodeAttrValue($configNode, 'group', ''),
                'type' => Xml::getNodeAttrValue($configNode, 'type', 'text'),
                'defaultval' => Xml::getNodeAttrValue($configNode, 'defaultval', ''),
                'tip' => Xml::getNodeAttrValue($configNode, 'tip', '')
            ];
            $config['param'] = Xml::getNodeAttrValueList($configNode, "param-");
            $configs[] = $config;
        }

        return $configs;
    }

    /**
     * 获取widgets节点
     *
     * @param \DOMElement $moduleNode
     * @return array
     */
    private static function _getWidgetsXmlNodeValueList(\DOMElement $moduleNode) {
        if (!$moduleNode) {
            return [];
        }
        $widgetsNodes = $moduleNode->getElementsByTagName("widgets");
        if (empty($widgetsNodes) || !$widgetsNodes->item(0)) {
            return [];
        }

        $widgets = [];
        $widgetNodes = $widgetsNodes->item(0)->getElementsByTagName("widget");
        foreach ($widgetNodes as $widgetNode) {
            $widget = [
                'name' => Xml::getNodeAttrValue($widgetNode, 'name'),
                'pushToModuleHome' => Xml::getNodeAttrValue($widgetNode, 'pushToModuleHome', true),
                'pushToGlobalHome' => Xml::getNodeAttrValue($widgetNode, 'pushToGlobalHome', false),
                'authority' => Xml::getNodeAttrValue($widgetNode, 'authority', false),
                'width' => Xml::getNodeAttrValue($widgetNode, 'width', '2'),
                'sort' => Xml::getNodeAttrValue($widgetNode, 'sort', 10)
            ];
            $widgets[] = $widget;
        }

        return $widgets;
    }

    /**
     * 获取任务配置节点
     *
     * @param \DOMElement $moduleNode
     * @return array
     */
    private static function _getCronTasksXmlNodeValueList(\DOMElement $moduleNode) {
        if (!$moduleNode) {
            return [];
        }
        $cronTasksNodes = $moduleNode->getElementsByTagName("cronTasks");
        if (empty($cronTasksNodes) || !$cronTasksNodes->item(0)) {
            return [];
        }

        $cronTasks = [];
        $cronTaskNodes = $cronTasksNodes->item(0)->getElementsByTagName("cronTask");
        foreach ($cronTaskNodes as $cronTaskNode) {
            $cronTask = [
                'name' => Xml::getNodeAttrValue($cronTaskNode, 'name'),
                'title' => Xml::getNodeAttrValue($cronTaskNode, 'title'),
                'cron' => Xml::getNodeAttrValue($cronTaskNode, 'cron'),
                'timeout' => Xml::getNodeAttrValue($cronTaskNode, 'timeout', 0),
                'overlap' => Xml::getNodeAttrValue($cronTaskNode, 'overlap', false),
            ];
            $cronTasks[] = $cronTask;
        }

        return $cronTasks;
    }

    /**
     * 获取microapp节点信息
     *
     * @param $moduleNode
     * @return array
     */
    private static function _getMicroapp(\DOMElement $moduleNode) {
        if (!$moduleNode) {
            return [];
        }
        $microappNodes = $moduleNode->getElementsByTagName("microapp");
        if (empty($microappNodes) || !$microappNodes->item(0)) {
            return [];
        }
        $microappNode = $microappNodes->item(0);
        $mobile = Xml::getNodeAttrValue($microappNode, "mobile");
        $pc = Xml::getNodeAttrValue($microappNode, "pc");
        $global = Xml::getNodeAttrValue($microappNode, "global", false);
        $title = Xml::getNodeAttrValue($microappNode, "title", "");
        if (empty($mobile) && empty($pc)) {
            return [];
        }
        if (empty($pc)) {
            $pc = $mobile;
        }
        if (empty($mobile)) {
            $mobile = $pc;
        }
        return [
            'pc' => $pc,
            'mobile' => $mobile,
            'global' => $global,
            'title' => $title
        ];
    }

}